package com.reider.log4AS3.layouts
{
	import com.reider.log4AS3.dataholder.Log;
	import com.reider.log4AS3.layouts.converters.ClassConverter;
	import com.reider.log4AS3.layouts.converters.Converter;
	import com.reider.log4AS3.layouts.converters.DateConverter;
	import com.reider.log4AS3.layouts.converters.LevelConverter;
	import com.reider.log4AS3.layouts.converters.MemoryConverter;
	import com.reider.log4AS3.layouts.converters.MessageConverter;
	import com.reider.log4AS3.layouts.converters.NewLineConverter;
	import com.reider.log4AS3.layouts.converters.RelativeTimeConverter;
	import com.reider.log4AS3.layouts.converters.TimeConverter;
	import com.reider.log4AS3.utils.MessageFormatter;
	import com.reider.log4AS3.utils.ObjectContainer;
	
	import flash.utils.Dictionary;
	
	/**
	 * This Layout can be used to format the message that gets added to the Appender.
	 * <br/><br/>
	 * Inbuilt patterns:<br/>
	 * 
	 * <b>%level</b> 	: The level of the log.<br/>
	 * <b>%date</b>		: The time when the log was fired.<br/>
	 * <b>%message</b>	: The log message.<br/>
	 * <b>%logger</b>	: The name of the logger.<br/>
	 * <b>%newline</b>	: A newline character<br/>
	 * <b>%memory</b>	: The current memory usage.<br/>
	 * <b>%relative</b>	: The relative time to the containing class in milliseconds when the log was fired.<br/>
	 * 
	 */
	public class PatternLayout extends Layout
	{
		private static const PATTERN:RegExp = /%(?P<name>\w+)/gmi;
		private static var converters:Dictionary = new Dictionary()
		
		/**
		 * @private
		 */
		private static var initialized:Boolean = init()
		
		private var _pattern:String
			
		//--------------------------------------------------------------------------
		//
		//  Geters, Seters
		//
		//--------------------------------------------------------------------------
		
		public function get pattern():String
		{
			return _pattern;
		}
		
		public function set pattern(value:String):void
		{
			_pattern = value;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		/**
		 * @private
		 */
		
		public function PatternLayout()
		{	
		}
		
		//--------------------------------------------------------------------------
	    //
	    //  Public methods
	    //
	    //--------------------------------------------------------------------------

		/**
		 * @private
		 */
		
		override public function format(log:Log):String
		{
			if(pattern){
				var patternParts:Array = getPatternParts( pattern )
				
				return applyPattern(pattern, patternParts, log)
			}else{
				return log.formattedText;
			}
		}
		
		//--------------------------------------------------------------------------
	    //
	    //  Private methods
	    //
	    //--------------------------------------------------------------------------
		
		private function applyPattern(patternStr:String, patternParts:Array, log:Log):String
		{
			var resultStr:String = patternStr;
			
			for(var i:int=0; i<patternParts.length; i++){
				var curPattern:String = String(patternParts[i]).toLowerCase()
				var converter:Converter = converters[ curPattern ] as Converter
				if(converter){
					resultStr = MessageFormatter.replace(
						resultStr, "%"+curPattern, converter.convert(log)
					)
				}
			}
			
			return resultStr;
		}
		
		
		private function getPatternParts(patternStr:String):Array
		{
			var patternParts:Array = new Array()
			
			var result:Object
			PATTERN.lastIndex = 0
			while (result = PATTERN.exec(patternStr)) {
				patternParts.push(result.name)
			}
			
			return patternParts
		}
		
		//--------------------------------------------------------------------------
	    //
	    //  Static methods
	    //
	    //--------------------------------------------------------------------------
	    
	    private static function init():Boolean
		{	
			addConverter(["level","le","l"], LevelConverter)
			addConverter(["date","d"], DateConverter)
			addConverter(["time","t"], TimeConverter)
			addConverter(["message","msg","m"], MessageConverter)
			addConverter(["class","logger","l", "c"], ClassConverter)
			addConverter(["newline","n"], NewLineConverter)
			addConverter(["memory","mem"], MemoryConverter)
			addConverter(["relative","r"], RelativeTimeConverter)
			
			return true
		}
		
		/**
		 * This method can be used to add a custom pattenr and a converter to that pattern.
		 * 
		 * @param patternParts An array containing string pattern names
		 * @param converterClass The converted assigned to the pattern
		 */

		public static function addConverter(patternParts:Array, converterClass:Class):void
		{
			var converter:* = new converterClass()
			if(!(converter is Converter)){
				throw new ArgumentError("converterClass must extend: com.reider.log4AS3.layouts.converters.Converter")
			}
			
			for(var i:int=0; i<patternParts.length; i++){
				converters[ patternParts[i] ] = converter
			}
		}
	}
}